
#Region "Using Directives"

Imports System
Imports System.Collections.Generic
Imports System.Text

#End Region

Namespace C4F.DevKit.Bluetooth

    Namespace ServiceAndDeviceDiscovery

        ''' <summary>
        ''' An abstract class which represents the Bluetooth service and 
        ''' contains general methods and properties which are 
        ''' required for communicating with client application.
        ''' </summary>
        Public MustInherit Class Service

#Region "Protected Fields"

            ''' <summary>
            ''' Queryset structure holds the information related. 
            ''' to the service.
            ''' </summary>
            Protected wsaQuerySet As QuerySet

            ''' <summary>
            ''' Represents the description of the service.
            ''' </summary>
            Protected m_description As String

            ''' <summary>
            ''' Represents the id of the service.
            ''' </summary>
            Protected m_id As ServiceId

            ''' <summary>
            ''' Represents the name of the service.
            ''' </summary>
            Protected m_name As String

            ''' <summary>
            ''' Represents the service record of the service.            
            ''' </summary>
            Protected m_serviceRecord() As Byte

            ''' <summary>
            ''' Represents the type of the service.
            ''' </summary>
            Protected m_type As ServiceType

#End Region

#Region "Internal Functions"

            ''' <summary>
            ''' Initializes a BluetoothService object.
            ''' Used for initializing a LocalService.
            ''' </summary>
            Friend Sub New()
            End Sub

            ''' <summary>
            ''' Initializes a BluetoothService object.
            ''' Used for initializing a RemoteService.
            ''' </summary>
            ''' <param name="querySet">Object of QuerySet class.</param>
            Friend Sub New(ByVal querySet As QuerySet)

                With Me

                    .wsaQuerySet = querySet

                    .m_id.ServiceIdType = ServiceIdType.TypeNil
                    .m_id.ServiceIdUnion = New ServiceIdUnion()
                    .m_type = ServiceType.UnknownProfile

                    .m_description = GetDescription(.wsaQuerySet)
                    .m_name = GetName(.wsaQuerySet)

                    ' Note: A call to GetServiceRecord also extracts the 
                    ' service id and type values from the service record.

                    .m_serviceRecord = GetServiceRecord(.wsaQuerySet)

                End With

            End Sub

#End Region

#Region "Private Functions"

            ''' <summary>
            ''' Extracts the service description.
            ''' </summary>
            ''' <param name="querySet">Queryset structure holds the information
            ''' related to the service.</param>
            ''' <returns>Returns the description of the service.</returns>
            Private Function GetDescription(ByVal querySet As QuerySet) As String

                Dim description As String = String.Empty

                If querySet.Comment IsNot Nothing Then

                    description = RegularExpressions.Regex.Replace(querySet.Comment, SDPMessages.PatternForNonPrintChars, _
                                                String.Empty, RegularExpressions.RegexOptions.IgnoreCase)

                End If

                Return description.Trim()

            End Function

            ''' <summary>
            ''' Extracts the service name.
            ''' </summary>
            ''' <param name="querySet">Queryset structure holds the information
            ''' related to the service.</param>
            ''' <returns>Returns the name of the service.</returns>
            Private Function GetName(ByVal querySet As QuerySet) As String

                Dim name As String = String.Empty

                If querySet.ServiceInstanceName IsNot Nothing Then

                    name = RegularExpressions.Regex.Replace(querySet.ServiceInstanceName, SDPMessages.PatternForNonPrintChars, _
                                        String.Empty, RegularExpressions.RegexOptions.IgnoreCase)

                End If

                Return name.Trim()

            End Function

            ''' <summary>
            ''' Extracts the service record.
            ''' </summary>
            ''' <param name="querySet">Queryset structure holds the information
            ''' related to the service.</param>
            ''' <returns>Returns the service record of the service.</returns>
            Private Function GetServiceRecord(ByVal querySet As QuerySet) As Byte()

                Try

                    Dim blob As New Blob()

                    blob = CType(Runtime.InteropServices.Marshal.PtrToStructure(querySet.Blob, GetType(Blob)), Blob)

                    Dim byteBlobData() As Byte = Nothing

                    If blob.Size <> 0 Then

                        byteBlobData = New Byte(CInt(blob.Size) - 1) {}

                        Runtime.InteropServices.Marshal.Copy(querySet.Blob, byteBlobData, 0, CInt(blob.Size))

                        ' Declare call back for BluetoothSdpEnumAttributes function.
                        Dim calbckEventHandler As New BluetoothHelper.CallbackEventHandler(AddressOf ParseAttribute)

                        ' Enumerates through the SDP record stream,
                        ' and calls the ParseAttribute function for each attribute 
                        ' in the record.

                        If Not BluetoothHelper.BluetoothSdpEnumAttributes(blob.BlobData, blob.Size, _
                                                                          AddressOf ParseAttribute, New IntPtr()) Then

                            Return Nothing

                        Else

                            Return byteBlobData

                        End If

                    Else

                        Return byteBlobData

                    End If

                Catch

                    Return Nothing

                End Try

            End Function

            ''' <summary>
            ''' Callback function for extracting attributes of the service 
            ''' from the service record.
            ''' </summary>
            ''' <param name="attribId">Identifies the current attribute in the
            ''' service record.</param>
            ''' <param name="valueStream">Represents the value associated with 
            ''' the attribute.</param>
            ''' <param name="streamSize">size in bytes of valueStream</param>
            ''' <param name="param">Optional.The context passed in from a previous call 
            ''' to the BluetoothSdpEnumAttributes function.</param>
            ''' <returns>Returns TRUE when the enumeration continues to the next 
            ''' attribute identifier found in the stream. Return FALSE
            ''' when enumeration of the record attribute identifiers should 
            ''' immediately stop.</returns>
            Private Function ParseAttribute(ByVal attribId As UInteger, ByVal valueStream As IntPtr, _
                                             ByVal streamSize As UInteger, ByVal param As IntPtr) As Boolean

                Dim element As New SdpElementData()

                Dim result As Integer = BluetoothHelper.BluetoothSdpGetElementData(valueStream, streamSize, element)

                If result <> 0 Then
                    BluetoothHelper.GetError()
                End If

                Select Case element.Type

                    Case SdpType.SdpTypeSequence

                        ' Represents the service class id i.e. type of the service.
                        If attribId = 1 Then

                            If element.Data.SequenceValue.Length > 0 Then

                                Dim sequenceData() As Byte = New Byte(CInt(element.Data.SequenceValue.Length) - 1) {}

                                Runtime.InteropServices.Marshal.Copy(element.Data.SequenceValue.Value, _
                                                                     sequenceData, 0, CInt(element.Data.SequenceValue.Length))

                                Dim length As Integer = 0
                                Dim hexValue As String = String.Empty

                                If sequenceData(0) = 53 Then

                                    Dim sizeDescriptor As Integer = sequenceData(1)

                                    ' Size of type is represented by 2 bytes.
                                    If sequenceData(2) = 25 Then
                                        length = 2
                                    Else
                                        ' Size of type is represented by length variable.
                                        length = sequenceData.Length - 3
                                    End If

                                    For index As Integer = 3 To length + 2

                                        ' Convert the value to hexadecimal. 
                                        Dim inum As Integer = sequenceData(index)
                                        hexValue = hexValue + String.Format(SDPMessages.HexFormatter, inum)

                                    Next

                                    Dim serviceType As String = String.Empty

                                    ' Append the base UUID. 
                                    If length = 2 Then

                                        serviceType = SDPMessages.GuidBeginValue + hexValue + SDPMessages.GuidEndValue

                                    Else

                                        ' Convert value to Guid format.
                                        hexValue = hexValue.Insert(8, SDPMessages.Seperator)
                                        hexValue = hexValue.Insert(13, SDPMessages.Seperator)
                                        hexValue = hexValue.Insert(18, SDPMessages.Seperator)
                                        hexValue = hexValue.Insert(23, SDPMessages.Seperator)

                                        serviceType = hexValue

                                    End If

                                    Me.m_type = New ServiceType(New Guid(serviceType), Nothing)

                                End If

                            End If

                        End If

                        Exit Select

                    Case SdpType.SdpTypeUuid

                        ' Represents the service id of the service.
                        If attribId = 3 Then

                            With Me

                                Select Case element.SpecificType

                                    Case SdpSpecificType.SdpStUuid16

                                        Dim uuid16Value As UShort = element.Data.Uuid16Value
                                        .m_id.ServiceIdType = ServiceIdType.TypeUint16
                                        .m_id.ServiceIdUnion.Uuid16Value = uuid16Value
                                        Exit Select

                                    Case SdpSpecificType.SdpStUuid32

                                        Dim uuid32Value As UInteger = element.Data.Uuid32Value
                                        .m_id.ServiceIdType = ServiceIdType.TypeUint32
                                        .m_id.ServiceIdUnion.Uuid32Value = uuid32Value
                                        Exit Select

                                    Case SdpSpecificType.SdpStUuid128

                                        Dim uuid128Value As Guid = element.Data.Uuid128Value
                                        .m_id.ServiceIdType = ServiceIdType.TypeUuid
                                        .m_id.ServiceIdUnion.Uuid128Value = uuid128Value
                                        Exit Select

                                End Select

                            End With

                        End If

                        Exit Select

                    Case Else

                        Exit Select

                End Select

                Return True

            End Function

#End Region

#Region "Public Properties"

            ''' <summary>
            ''' Gets the service name.
            ''' </summary>
            Public ReadOnly Property Name() As String

                Get
                    Return Me.m_name
                End Get

            End Property

            ''' <summary>
            ''' Gets the service description.
            ''' </summary>
            Public ReadOnly Property Description() As String

                Get
                    Return Me.m_description
                End Get

            End Property

            ''' <summary>
            ''' Gets the service record.
            ''' </summary>
            Public ReadOnly Property ServiceRecord() As Byte()

                Get
                    Return Me.m_serviceRecord
                End Get

            End Property

            ''' <summary>
            ''' Gets the service type.
            ''' </summary>
            Public ReadOnly Property Type() As ServiceType

                Get
                    Return Me.m_type
                End Get

            End Property

            ''' <summary>
            ''' Gets the service id.
            ''' </summary>
            Public ReadOnly Property Id() As ServiceId

                Get
                    Return Me.m_id
                End Get

            End Property

#End Region

        End Class

    End Namespace

End Namespace
